// A-Carrier

const process = require("process");
var http = require("http");
const fs = require("fs");
const { URL } = require('url');

const Properties = require("./modules/properties.js");
const Log = require("./modules/logger.js");

process.argv = process.argv.slice(2);
function startDownload() {

	if (process.argv.length == 0 || process.argv[0] == "-help" || process.argv[0] == "-?") {
		console.log("Usage: node carrier [options]\nOptions:\n    " +
			([
				"-?             Show this text(Must is first arg)",
				"-help          Show this text(Must is first arg)",
				"-Oname=value   Set or overlay option(carrier.properties)"
			].join('\n    ')));
		return;
	}

	var prop = new Properties(fs.readFileSync("carrier.properties").toString());
	process.argv.forEach(function self(i) {
		if (i.startsWith("-O")) {
			i = i.slice(2);
			i = i.split("=");
			if (i.length < 2) throw new SyntaxError(i);
			prop.put(i[0], i.slice(1).join("="));
		} else {
			throw new SyntaxError("Arguments has a error at '" + i + "'");
		}
	});

	var filesize = 0;
	var url = new URL(prop.get("url"));
	if (url.protocol == "https:") http = require("https");
	if (url.protocol == "http:") http = require("http");
	var filename = url.pathname;
	filename = filename.split("/")[filename.split("/").length - 1];
	if (!fs.existsSync(prop.get("save"))) fs.mkdirSync(prop.get("save"));
	if (!fs.existsSync(prop.get("cache"))) fs.mkdirSync(prop.get("cache"));

	var opt = JSON.parse(prop.get("options"));
	var fsOpt = Object.create(opt);
	fsOpt.method = "HEAD";
	var fsReq = http.request(url, fsOpt, function (res) { });
	fsReq.on('error', (e) => {
		Log.e('Network', 'Failed to get file size: ' + e.toString());
	}); var func
	fsReq.on('response', (res) => {
		if (res.statusCode != 200) {
			var red = res.statusCode >= 300 && res.statusCode < 400;
			if (red && res.headers['location'] != null) {
				Log.w("Server", "Redirect to " + res.headers['location'] + " by [" + res.statusCode + "][" + res.statusMessage + "]");
				process.argv.push("-Ourl=" + res.headers['location']);
				startDownload();
			} else {
				Log.e("Server", "Failed to get file size: HEAD failed[" + res.statusCode + "][" + res.statusMessage + "]");
			}
			return;
		}
		if (res.headers['content-length'] != null) {
			filesize = Number.parseInt(res.headers['content-length']);
			Log.i("Server", "Geted file size from server: " + filesize + "Byte");
			if (prop.get("disableRange") == "true") {
				download();
			} else {
				if (res.httpVersion != "1.1") {
					Log.w("Range download need http version 1.1, server version is " + res.httpVersion);
					download();
				} else {
					downloadRange();
				}
			}
		} else {
			Log.w("Server", "Server not support content length");
			download();
		}
	})
	if (prop.get("body") != "...") {
		var uploadData = prop.get("body");
		if (uploadData.startsWith("file:")) uploadData = fs.readFileSync(uploadData.slice(5)).toString();
		fsReq.write(uploadData);
	}
	fsReq.end();

	function download() {
		var req = http.request(url, opt, getDownloadRangeCallbackWrap(0, (data) => {
			try {
				fs.writeFileSync(prop.get("save") + "/" + filename, data);
				console.info("DOWNLOAD SUCCESSFUL.");
			} catch (err) {
				Log.e("IO", "Failed to write file");
				console.error("DOWNLOAD FAILED.");
			}
		}));
		req.on('error', (e) => {
			Log.e('Network', 'Failed to get file: ' + e.toString());
		});
		req.end();
	}

	function downloadRange() {

	}

	function getDownloadRangeCallbackWrap(sectionId, end) {
		var wrap = function DownloadRangeCallbackWrap(res) {
			var section = DownloadRangeCallbackWrap.sectionId;
			var end_event = DownloadRangeCallbackWrap.end_event;
			var buff = Buffer.alloc(0);
			res.on('data', (data) => {
				var recv = Buffer.alloc(buff.length + data.length);
				for (var i = 0; i < buff.length; i++) recv[i] = buff[i];
				for (var i = 0; i < data.length; i++) recv[i + buff.length] = data[i];
				buff = recv;
				Log.i("Receiver", "Receive section " + section + " for data[" + data.length + "Byte]");
			});
			res.on('end', () => {
				try {
					fs.writeFileSync(prop.get("cache") + "/s_" + section + ".carriercache", buff);
				} catch (err) {
					Log.e("IO", "Failed to write section " + section + " to cache.");
					throw new Error("Failed to write data");
				}
				end_event(buff);
			});
		}
		wrap.sectionId = sectionId;
		wrap.end_event = end;
		return wrap;
	}
}
startDownload();
